﻿using Ec.NETCoreFramework.Common.Core;
using Ec.NETCoreFramework.Common.Core.Helpers;
using Ec.NETCoreFramework.Common.Core.Utilities;
using System;
using System.Collections.Generic;
using System.Text.RegularExpressions;

namespace Ec.NETCoreFramework.Common.ValidateSign
{
    public class ValidateParameter
    {
        private SignConfig _signConfig;
        public ValidateParameter(SignConfig signConfig)
        {
            this._signConfig = signConfig;
        }
        /// <summary>
        /// 校验参数
        /// </summary>
        /// <param name="requestString"></param>
        /// <returns></returns>
        public SysParameter Validate(string requestString)
        {
            if (string.IsNullOrEmpty(requestString))
            {
                var ex = new Exception("请求参数错误,缺少接口必须参数。");
                ex.HResult = 4001;
                throw ex;
            }

            SysParameter para = ParseOpenApiParameter(requestString);

            para.RequestContent = requestString;

            // 参数有效性校验
            ValidParameter(para);

            // 对时间戳进行验证
            ValidTimestamp(para);

            //验证签名，防止参数被修改
            ValidSign(para);

            return para;
        }
        /// <summary>
        /// 解析请求参数
        /// </summary>
        /// <param name="requestString"></param>
        /// <returns></returns>
        public SysParameter ParseOpenApiParameter(string requestString)
        {
            SysParameter sysPara = new SysParameter();
            Regex reParameter = new Regex(@"(?<paraKey>[^=|^&]+)=(?<paraValue>[^\&$]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);

            Regex reSpace = new Regex(@"[\s]*", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.Multiline);

            requestString = reSpace.Replace(requestString, string.Empty);

            string paraKey = null, paraValue = null;

            foreach (Match m in reParameter.Matches(requestString))
            {
                paraKey = m.Groups["paraKey"].Value;
                paraValue = m.Groups["paraValue"].Value;

                // 签名数据
                if (string.Compare("sign", paraKey, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    sysPara.Sign = paraValue;
                }
                // 请求时间戳
                else if (string.Compare("timestamp", paraKey, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    long tstamp = 0;
                    long.TryParse(paraValue, out tstamp);
                    sysPara.Timestamp = tstamp;

                }
                // 版本号
                else if (string.Compare("v", paraKey, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    sysPara.Version = paraValue;
                }
                // 应用key
                else if (string.Compare("appid", paraKey, StringComparison.OrdinalIgnoreCase) == 0)
                {
                    sysPara.AppId = paraValue;
                }
                if (sysPara.Properties == null)
                    sysPara.Properties = new Dictionary<string, object>(StringComparer.OrdinalIgnoreCase);


                if (!sysPara.Properties.ContainsKey(paraKey))
                {
                    sysPara.Properties.Add(paraKey, paraValue);
                }
            }

            return sysPara;
        }
        /// <summary>
        /// 
        /// </summary>
        /// <param name="parameter"></param>
        public void ValidParameter(SysParameter parameter)
        {
            var messaage = string.Empty;
            if (string.IsNullOrEmpty(parameter.AppId))
            {
                messaage = "缺少必须的Appid参数";
                var ex = new Exception(messaage);
                ex.HResult = 4001;
                throw ex;
            }

            if (!_signConfig.AppSecret.ContainsKey(parameter.AppId))
            {
                messaage = "请求的参数错误AppId不正确";
                var ex = new Exception(messaage);
                ex.HResult = 4001;
                throw ex;
            }

            if (parameter.Timestamp == 0)
            {
                messaage = "缺少必须的Timestamp参数";
                var ex = new Exception(messaage);
                ex.HResult = 4001;
                throw ex;
            }

            if (string.IsNullOrEmpty(parameter.Sign))
            {
                messaage = "缺少必须的Sign参数";
                var ex = new Exception(messaage);
                ex.HResult = 4001;
                throw ex;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parameter"></param>
        public void ValidSign(SysParameter parameter)
        {
            bool ret = false;
            // 1. 根据appid获取出对应的Secret
            if (!_signConfig.AppSecret.ContainsKey(parameter.AppId))
            {
                var ex = new Exception("appid无效");
                ex.HResult = 4005;
                throw ex;
            }
            string secret = _signConfig.AppSecret[parameter.AppId];

            var dic = parameter.Properties;

            if (dic != null)
            {
                Dictionary<string, object> sortDic = dic.SortFilter(new string[] { "sign", "secret" });

                string sign = Encrypt.Md5By32(sortDic.ToLinkString() + "&secret=" + secret);
                // 3. 对比签名有效性
                ret = (sign == parameter.Sign);
            }

            if (!ret)
            {
                var ex = new Exception("签名无效");
                ex.HResult = 4003;
                throw ex;
            }
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="parameter"></param>
        public void ValidTimestamp(SysParameter parameter)
        {
            bool ret = false;
            long currentStamp = Utils.GetTimstamp();
            long absDiff = System.Math.Abs(currentStamp - parameter.Timestamp);
            // 请求的时间差为上下180秒以内
            ret = (absDiff < 180);
            if (!ret)
            {
                var ex = new Exception("接口调用时间戳有误");
                ex.HResult = 4003;
                throw ex;
            }
        }
    }
}
